<?php

  define("W2S_FEDEX_CARRIER", 1);
  define("W2S_PUROLATOR_CARRIER", 2);
  define("W2S_DHL_CARRIER", 4);
  define("W2S_CANADA_POST_CARRIER", 5);  
  define("W2S_UPS_CARRIER", 6);
  
  define("W2S_FEDEX_DIRECT_CARRIER", 50);
  define("W2S_CANADA_POST_DIRECT_CARRIER", 51);
  define("W2S_UPS_DIRECT_CARRIER_US", 52);
  define("W2S_UPS_DIRECT_CARRIER_CA", 53);
  define("W2S_UPS_DIRECT_CARRIER_INT", 54);
  
  define("W2S_ORDER_STATUS_LABEL_GENERATED", 1); 
  
  define("W2S_SERVICE_URL", "http://way2ship.com/w2s/service");

  
  
  class Way2ShipSettings {
	  	   	
	  var $w2s_services = array(
	    W2S_FEDEX_CARRIER => array(1 => "Priority (US, CA)", 2 => "Overnight (US, CA)", 3 => "Ground (US, CA)", 
	  	28 => "Standard Overnight (US)", 29 => "Second Day (US)"),
	  	 
	    W2S_PUROLATOR_CARRIER => array(4 => "Purolator Air", 5 => "Purolator Air 9AM", 6 => "Purolator Air 1030",
	  	7 => "Puroletter", 8 => "Puroletter 9AM", 9 => "Puroletter 1030", 10 => "PuroPak", 11 => "PuroPak 9AM", 
	  	12 => "PuroPak 1030", 13 => "Purolator Ground", 19 => "Purolator Ground 9AM", 20 => "Purolator Ground 1030"),
	    
	  	W2S_DHL_CARRIER => array(100 => "DHL Express", 101 => "DHL Express 10:30AM", 102 => "DHL Express Saturday",
	  	103 => "DHL Express Second Day", 104 => "DHL Next Afternoon", 105 => "DHL Ground", 106 => "DHL International Express"), 
	    
	  	W2S_CANADA_POST_CARRIER => array (500 => "Canada Post Priority", 501 => "Canada Post Xpress", 502 => "Canada Post Expedited",
	  	503 => "Canada Post Regular", 504 => "Canada Post Xpress USA", 505 => "Canada Post Xpress Intl", 506 => "Canada Post Air Intl",
	  	507 => "Canada Post Surface Intl", 508 => "Canada Post Expedited USA"), 
	    
	  	W2S_UPS_CARRIER => array(600 => "UPS Express", 601 => "UPS Expedited", 602 => "UPS Worldwide Express", 
	  	603 => "UPS Worldwide Expedited", 604 => "UPS Standard (Ground)", 605 => "UPS Express Early A.M.", 
	  	606 => "UPS Three-Day Select", 607 => "UPS Saver", 608 => "UPS Ground", 609 => "Next Day Saver", 
	  	610 => "Worldwide Express Plus", 611 => "Second Day Air A.M."),
	  	
	  	W2S_CANADA_POST_DIRECT_CARRIER => array (1010 => "Regular", 1020 => "Expedited", 1030 => "Xpresspost",
	  	1040 => "Priority", 2005 => "Pack Surface USA", 2015 => "Pack Air USA", 2020 => "Expedited parcel USA", 2025 => "Expedited USA",
	  	2030 => "Xpresspost USA", 2040 => "Priority USA", 2050 => "Priority Pak USA", 3005 => "Pack Surface Int", 
	  	3010 => "Parcel Surface Int", 3015 => "Pack Air Int", 3020 => "Parcel Air Int", 3025 => "Xpresspost Int",
	  	3040 => "Priority Int", 3050 => "Priority Pak Int"),
	  	
	  	W2S_FEDEX_DIRECT_CARRIER => array(5000 => "First Overnight", 5001 => "Priority Overnight", 5002 => "Standard Overnight", 
	  	5003 => "2nd Day",  5004 => "Express Saver", 5005 => "Fedex Ground", 5006 => "Ground Home Delivery", 
	  	5007 => "Intl First", 5008 => "Intl Priority", 5009 => "Intl Economy", 5010 => "Europe First Intl"),	  	
	  	
	  	W2S_UPS_DIRECT_CARRIER_US => array(7001 => "Next Day Air", 7002 => "Second Day Air", 7003 => "Ground", 
	  	7004 => "Three-Day Select",  7005 => "Next Day Air Saver", 7006 => "Next Day Air Early A.M.", 7007 => "Second Day Air A.M.", 
	  	7008 => "Saver", 7009 => "Standard", 7010 => "Worldwide Express", 7011 => "Worldwide Expedited", 7012 => "Worldwide Express Plus"),	  	
	  	
	  	W2S_UPS_DIRECT_CARRIER_CA => array(7050 => "Express", 7051 => "Expedited", 7052 => "Worldwide Express", 
	  	7053 => "Worldwide Expedited",  7054 => "Standard", 7055 => "Three-Day Select", 7056 => "Saver", 
	  	7057 => "Express Early A.M.", 7058 => "Worldwide Express Plus", 7059 => "Saver"),	  	
	  	
	  	W2S_UPS_DIRECT_CARRIER_INT => array(7100 => "Express", 7101 => "Expedited", 7102 => "Standard", 
	  	7103 => "Worldwide Express Plu", 7104 => "Saver")
	  	);
	  	
	  function getServicesByCarrier($carrier) {
	  	return $this->w2s_services[$carrier];
	  }
  }
  	
/**
 * Main wrapper class for Way2Ship electroni shipping services
 * Please visit waytoship.com for more information.
 */
  class Way2Ship {
	var $way2ShipHelper;    


	/**
	 * Constructs the Way2Ship class
	 * Expects WayToShip username and password
	 * Please visit waytoship.com for more information..
	 *
	 * @param unknown_type $user_name WayToShip username
	 * @param unknown_type $password WayToShip password
	 * @return Way2Ship
	 */
    function Way2Ship($user_name, $password, $timeout = NULL) {
    	$this->way2ShipHelper = new Way2ShipHelper($user_name, $password, $timeout);
    }
     
    /**
     * Returns WayToShip shipping quotes on the quoteOrder
     * $servicesEnabled is a whitelist array of service IDs to include
     * if a quote is available 
     * 
     * @param W2SQuoteOrder $quoteOrder The object encapsulating all the order details
     * @param array $servicesEnabled Service IDs to include in the response
     * @return W2SQuotes The quotes from WayToShip server
     */
    function getQuotes($quoteOrder, $carrierID = NULL, $servicesEnabled = NULL) {
		return $this->way2ShipHelper->getQuotes($quoteOrder, $carrierID, $servicesEnabled);		
    }

    /**
     * Returns an XML representation of the orders to ship to send
     * to Way2Ship shipping service.
     *
     * @param W2SShipOrder[] $shipOrders
     * @param string $serviceID
     * @return string 
     */
    function buildOrdersXML($shipOrders) {
    	return $this->way2ShipHelper->buildOrdersXML($shipOrders);
    }
  
  	function getOrdersUpdates($ordersUpdateXML) {
  		return $this->way2ShipHelper->getOrdersUpdates($ordersUpdateXML);	
  	}
  }
  
  /**
   * A container class for a shipping order.
   */ 
  class W2SShipOrder {
  	var $fromLocation, $fromContact, 
  		$toLocation, $toContact, 
  		$shipOrderItems, $serviceID,
  		$subTotal, $total, $shippingCharge,
  		$orderID;
  		
  	function W2SShipOrder($orderID, $fromLocation, $fromContact, 
  		$toLocation, $toContact, $serviceID) {
  			
  		$this->orderID = $orderID;
  		$this->fromLocation = $fromLocation; 
  		$this->fromContact = $fromContact; 
  		$this->toLocation = $toLocation; 
  		$this->toContact = $toContact;
  		$this->serviceID = $serviceID;
  		 
  	}
  	
     /**
     * Adds a single order item to the order. Please call subsequently on all order items
     * @param W2SShipOrderItem $shipOrderItem The order item containing a single cart line information 
     */
    function addOrderItem($shipOrderItem) {    	
    	// appending with auto-increment syntax
    	$this->shipOrderItems[] = $shipOrderItem;	    	
    } 		
  }
  
  /**
   * Ship location representation
   */
  class W2SShipLocation extends W2SQuoteLocation {
  	/**
  	 * Constructor
  	 * @param string $zip
  	 * @param string $state
  	 * @param string $country
  	 * @param string $city
  	 * @param string $address
  	 * @param string $address2
  	 * @return W2SShipLocation
  	 */
  	function W2SShipLocation($zip, $state, $country, $address, $address2, $city) {
  		parent::W2SQuoteLocation($zip, $state, $country, $address, $address2, $city);
  	}
  }
  
  /**
   * Ship contact representation
   */
  class W2SShipContact {
  	var $company, $name, $phone, $email;
  	/**
  	 * Constructor
  	 * @param string $company
  	 * @param string $name
  	 * @param string $phone
  	 * @param string $email
  	 * @return W2SShipContact
  	 */
  	function W2SShipContact($company, $name, $phone, $email) {
  		$this->company = $company; 
  		$this->name = $name;
  		$this->phone = $phone;
  		$this->email = $email;
  	}
  }


	/**
	 * Order Item representation  
	 *
	 */	  
  class W2SShipOrderItem extends W2SQuoteOrderItem {
  	/**
  	 * Constructor
  	 * @param float $weight
  	 * @param float $price
  	 * @param int $quantity
  	 * @param string $modelName
  	 * @return W2SShipOrderItem
  	 */
  	function W2SShipOrderItem($weight, $price, $quantity, $modelName) {
  		parent::W2SQuoteOrderItem($weight, $price, $quantity, $modelName);	
  	}  	
  }  
  
  /**
   * A container class for a quote order.
   * Includes origin (merchant's) and destination (customer's) addresses,
   * items info from the shopping cart and order total (for insurance calculations) 
   */  
  class W2SQuoteOrder {
  	var $origin_address, $destination_address, $order_items, $total, $cart_url;

  	/**
  	 * Constructor for W2SQuoteOrder
  	 *
  	 * @param W2SQuoteLocation $origin_address The merchants' address to ship from
  	 * @param W2SQuoteLocation $destination_address The customers' address to ship to 
  	 * @param numeric $total The total value of the order
  	 * @return W2SQuoteOrder
  	 */
    function W2SQuoteOrder($origin_address, $destination_address, $total) {
    	$this->origin_address = $origin_address;
    	$this->destination_address = $destination_address;
    	$this->total = $total;   	    	    	
    }
    
    /**
     * Adds a single order item to the order. Please call subsequently on all cart items
     *
     * @param W2SQuoteOrderItem $quoteOrderItem The order item containing a single cart line information 
     */
    function addOrderItem($quoteOrderItem) {    	
    	// appending with auto-increment syntax
    	$this->order_items[] = $quoteOrderItem;	    	
    }
  }

  
/**
 * Defines an address for a quote service
  */  
  class W2SQuoteLocation {
	var $state, $zip, $country;
	var $address, $address2, $city;
	/**
	 * Constructor  
	 *
	 * @param string $state State or Province
	 * @param string $zip Zip or Postal code
	 * @param string $country Standard two letters country abbreviation
	 * @param string $city optional City 
	 * @return W2SQuoteLocation
	 */  	    
    function W2SQuoteLocation($zip, $state, $country, $address1 = '', $address2 = '', $city = '') {
    	$this->address = $address1;
    	$this->address2 = $address2;    	
    	$this->city = $city;	
    	$this->zip = $zip;    	
    	$this->state = $state;
    	$this->country = $country;
    }
  }  

  /**
   * Defines a single order item as an item in cart
   * Imperial measurment and weight units by default
   * Weight is mandatory while dimensions are optional.
   * Please note that passing dimensions is recomended 
   * as it will result in a better quote accuracy. 
   */
  class W2SQuoteOrderItem { 
	var $weight, $weightUnitsMetric,
	$price, $quantity, $modelName,
	$width, $length, $height, $dimUnitsMetric;
										
	/**
	 * Constructor
	 *
	 * @param float $weight Wight of the single item. Default units are lbs and can be changed later
	 * @param float $price The price of a single unit. Please use the currency your account is configured for 
	 * @param int $quantity The number of units in the cart
	 * @param string $modelName The model name (code)
	 * @return W2SQuoteOrderItem
	 */
	function W2SQuoteOrderItem($weight, $price, $quantity, $modelName) {

    	$this->weight = $weight;
    	$this->price = $price;
    	$this->quantity = $quantity;
    	$this->modelName = $modelName;
		$this->weightUnitsMetric = FALSE;
		$this->dimUnitsMetric = FALSE;
			
		// preset to zero dimensions to enable 
		// dimensional estimations on the server 
		// if no dimensions are provided by the cart 
		$this->width = 0;
		$this->length = 0; 
		$this->height = 0;
    }
    
    /**
     * Sets dimensions of the item.
     * Default dimentions are in inches. Call setDimMetricUnits() to change
     * @param int $width Packages product width.
     * @param int $length Packages product length.
     * @param int $height Packages product height.
     */
    function setDimensions($width, $length, $height) {
    	$this->width = $width;
    	$this->length = $length;
    	$this->height = $height;
    }
    
    /**
     * Change dimension units to inches
     */
    function setDimImperialUnits() {
    	$this->dimUnitsMetric = false;	
    }
    
    /**
     * Change dimension units to cm.
     */    
    function setDimMetricUnits() {
    	$this->dimUnitsMetric = true;	
    }

    /**
     * Change weight units to lbs.
     */    
    function setWeightImperialUnits() {
    	$this->weightUnitsMetric = false;	
    }

    /**
     * Change weight units to kg.
     */        
    function setWeightMetricUnits() {
    	$this->weightUnitsMetric = true;	
    }

    function getWeightUnit() {
    	if ($this->weightUnitsMetric) {
    		return "kg";    		
    	} else {
    		return "lb";
    	}
    }
    
    function getDimUnit() {
    	if ($this->dimUnitsMetric) {
    		return "cm";	
    	} else {
    		return "in";
    	}
    }
    
  }  

  /**
   * Container for the returned shipping quotes
   * Please check the $isError variable first to ensure 
   * no error has occured. If $isError returns true $errorMessage is available.
   * If no error occured you can get the $quotes array. 
   */
  class W2SQuotes {
  	var $quotes = array(), $isError, $errorMessage;

  	function W2SQuotes($isError) {
  		$this->isError = $isError;	
  	}
  	
  	function addQuote($quote) {
  		// append a quote
  		$this->quotes[] = $quote;	
  	}
  }

  /**
   * A single shipping quote
   */
  class W2SQuote {
	var $carrierId, $carrierName, $serviceId, $serviceName, 
	$transitDays, $totalCharge, $estimatedDeliveryDate;
	
    function compare($q1, $q2) {
    	$price1 = $q1->totalCharge;
    	$price2 = $q2->totalCharge;
    	
         if ($price1 == $price2) {
            return 0;
        }
        return ($price1 > $price2) ? +1 : -1;   		
    }	
  }  

  /**
   * Reperesents a single order update information
   *
   */
  class W2SOrderUpdate {
  	var $orderID, $orderStatus, $trackingNumber, $trackingURL;
	
  	/**
  	 * Constructor
  	 *
  	 * @param string $orderID
  	 * @param int $orderStatus
  	 * @return W2SOrderUpdate
  	 */
  	function W2SOrderUpdate($orderID, $orderStatus) {
  		$this->orderID = $orderID;
  		$this->orderStatus = $orderStatus;
  	}
  }
  
//------------------------------------------------------------------------
// Below are the implementation classes. Please do not call directly.  
//------------------------------------------------------------------------
  class Way2ShipHelper {
	var $user_name, $password, $timeout = 15;    

    function Way2ShipHelper($user_name, $password, $timeout) {
    	$this->user_name = $user_name;
    	$this->password = $password;
    	if (is_int($timeout)) {
    		$this->timeout = $timeout;
    	}   	
    }

    function getQuotes($quoteOrder, $carrierID, $servicesEnabled) {
		$requestXML = $this->buildShippingQuoteRequest($quoteOrder);
		$md5Hash = md5($requestXML);
		$cache = new W2SCache();		
		if (!$cache->existsInCache($md5Hash)) {		
			$quotes  = $this->getQuotesFromServer($requestXML);
			if (!$quotes->isError) {			
				$cache->putInCache($md5Hash, $quotes);
			}	 
		} else {			
			$quotes = $cache->getFromCache($md5Hash);	
		}	
		// there might be is a subset of services the cart is interested in
		$filtered_quotes = $this->filterQuotes($quotes, $carrierID, $servicesEnabled);
		return $this->sortQuotes($filtered_quotes);

    }
    
    function buildOrdersXML($shipOrders) {
	//print_r($shipOrders);  
    $ordersXml;    

    for ($reqID = 0; $reqID < count($shipOrders); $reqID++) {    	
    	$ordersXml .= $this->createOrderShippingRequest($shipOrders[$reqID], $reqID) . "\n";	
    }
    	
	$envelope = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<way2ship-request xmlns="http://www.way2ship.com/service/shipping" 
	username="{$this->user_name}" password="{$this->password}">	
	
    $ordersXml
    
</way2ship-request>
EOT;

	return $envelope;
    }

	function getOrdersUpdates($ordersUpdateXML) {
		
		$updates = $this->parse_updates_xml($ordersUpdateXML);
		$ordersUpdates = array();		
		foreach($updates as $update) {
			if ($update['status'] == 'LabelGenerated') {
				//the only type we can process at the moment		
				$orderUpdate = new W2SOrderUpdate($update['orderID'], W2S_ORDER_STATUS_LABEL_GENERATED);
				$orderUpdate->trackingNumber = $update['trackingNums'];
				$trackingNums='';
				foreach ($update['trackingNums'] as $trackingNum) {
					$trackingNums .= $trackingNum . ", ";	
				}
				$orderUpdate->trackingNumber = substr($trackingNums, 0, sizeof($trackingNums) - 3); // cut last ', '				
				$orderUpdate->trackingURL = $update['trackingURL'];
				$ordersUpdates[] = $orderUpdate;		
			}
		}				
		return $ordersUpdates;

	}
    
    function createOrderShippingRequest($shipOrder, $reqID) {
   	
   	$orderInfoXML = $this->createOrderInfoXML($shipOrder);
   	
	$orderShipping = <<<EOT
	<shipping-request 
		requestId="$reqID" 
		serviceId="{$shipOrder->serviceID}"
		storeGeneratedContent="onServer"
		>

		<from>
			<contact>
				<company><![CDATA[{$shipOrder->fromContact->company}]]></company> 
				<name><![CDATA[{$shipOrder->fromContact->name}]]></name>
				<phone><![CDATA[{$shipOrder->fromContact->phone}]]></phone> 
				<email><![CDATA[{$shipOrder->fromContact->email}]]></email>
			</contact>
			<address>
				<address><![CDATA[{$shipOrder->fromLocation->address}]]></address> 
				<address2><![CDATA[{$shipOrder->fromLocation->address2}]]></address2>
				<city><![CDATA[{$shipOrder->fromLocation->city}]]></city> 
				<state><![CDATA[{$shipOrder->fromLocation->state}]]></state> 
				<zip><![CDATA[{$shipOrder->fromLocation->zip}]]></zip> 
				<country><![CDATA[{$shipOrder->fromLocation->country}]]></country>
			</address>
		</from>

		<to>
			<contact>
				<company><![CDATA[{$shipOrder->toContact->company}]]></company> 
				<name><![CDATA[{$shipOrder->toContact->name}]]></name>
				<phone><![CDATA[{$shipOrder->toContact->phone}]]></phone> 
				<email><![CDATA[{$shipOrder->toContact->email}]]></email>
			</contact>
			<address>
				<address><![CDATA[{$shipOrder->toLocation->address}]]></address> 
				<address2><![CDATA[{$shipOrder->toLocation->address2}]]></address2>
				<city><![CDATA[{$shipOrder->toLocation->city}]]></city> 
				<state><![CDATA[{$shipOrder->toLocation->state}]]></state> 
				<zip><![CDATA[{$shipOrder->toLocation->zip}]]></zip> 
				<country><![CDATA[{$shipOrder->toLocation->country}]]></country>
			</address>
		</to>
		
		$orderInfoXML

	</shipping-request>
EOT;

	  return $orderShipping;   	 
    }
    
    function createOrderInfoXML($shipOrder) {
    	
    $packXml;
	foreach($shipOrder->shipOrderItems as $order_item) {

			$width = ceil($order_item->width);
			$length = ceil($order_item->length);
			$height = ceil($order_item->height);
	
			$weight = $order_item->weight;
			$weightUnit = $order_item->getWeightUnit();
			$dimUnit = $order_item->getDimUnit();
	
		$packXml .=<<<EOT
			<product quantity="{$order_item->quantity}" price="{$order_item->price}">
				<package weight="$weight" measurementUnits="{$weightUnit}">
					<size width="$width" length="$length" height="$height" measurementUnits="{$dimUnit}" />
				</package>	
			</product>
EOT;
	}

	$shipmentXml =<<<EOT
	<order cartOrderId="{$shipOrder->orderID}" 
		subTotal="{$shipOrder->subTotal}" shippingCharge="{$shipOrder->shippingCharge}" total="{$shipOrder->total}">
	<products>
		$packXml
	</products>
	</order> 
EOT;

	return $shipmentXml;   	
    }
       
    function buildShippingQuoteRequest($quoteOrder) {
		// packages
		//
		$packagesXml = $this->createPackagesXml($quoteOrder->order_items, $quoteOrder->total);

		$eshipper_request = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<way2ship-request xmlns="http://www.way2ship.com/service/shipping" 
	username="{$this->user_name}" password="{$this->password}">	
	<quote-request requestId="{$quoteOrder->cart_url}">	
		<from>
			<location>
				<state><![CDATA[{$quoteOrder->origin_address->state}]]></state>
				<zip><![CDATA[{$quoteOrder->origin_address->zip}]]></zip>
				<country><![CDATA[{$quoteOrder->origin_address->country}]]></country>
			</location>
		</from>	
		<to>
			<location>
				<address><![CDATA[{$quoteOrder->destination_address->address}]]></address> 
				<address2><![CDATA[{$quoteOrder->destination_address->address2}]]></address2>
				<city><![CDATA[{$quoteOrder->destination_address->city}]]></city> 				
				<state><![CDATA[{$quoteOrder->destination_address->state}]]></state>
				<zip><![CDATA[{$quoteOrder->destination_address->zip}]]></zip>
				<country><![CDATA[{$quoteOrder->destination_address->country}]]></country>
			</location>
		</to>		
		<!-- packages start -->			
		$packagesXml
		<!-- packages end -->
	</quote-request>
</way2ship-request>
EOT;
		return $eshipper_request;
    }

    function createPackagesXml($order_items, $total) {
	
		$packXml;
		foreach($order_items as $order_item) {
			$weight_units = $order_item->getWeightUnit();
			$dim_units = $order_item->getDimUnit();
			
			$width = ceil($order_item->width);
			$length = ceil($order_item->length);
			$height = ceil($order_item->height);
	
			$weight = $order_item->weight;

	
		$packXml .=<<<EOT
			<product quantity="{$order_item->quantity}" price="{$order_item->price}">
				<package weight="$weight" measurementUnits="$weight_units">
					<size width="$width" length="$length" height="$height" measurementUnits="$dim_units" />
				</package>	
			</product>
EOT;
	}

	$shipmentXml =<<<EOT
	<order subTotal="$total">
	<products>
		$packXml
	</products>
	</order> 
EOT;

		return $shipmentXml;
	    	
    }
    
    function getQuotesFromServer($requestXML) {
    	
	 	$header_array = array(
				"POST / HTTP/1.0",
				"Content-type: application/x-www-form-urlencoded",
				"Content-length: ".strlen($requestXML)
		);
	

	  	$ch = curl_init();
	  	curl_setopt($ch, CURLOPT_URL, W2S_SERVICE_URL);
	  	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	  	curl_setopt($ch, CURLOPT_HEADER, 0);
	  	curl_setopt ($ch, CURLOPT_HTTPHEADER, $header_array);
	  	curl_setopt($ch, CURLOPT_POST, 1);
	  	curl_setopt($ch, CURLOPT_FAILONERROR, 1);	  	 
	  	curl_setopt($ch, CURLOPT_POSTFIELDS, $requestXML);
	  	curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);

	  	$xmlResponse = curl_exec ($ch);
	  	$comm_error = curl_error($ch);	  	
	  	curl_close ($ch);	  	

	  	if ($xmlResponse === FALSE) {
	  		//communication error occured  		
			$quotes = new W2SQuotes(true);			
			$quotes->errorMessage = "Error communicating with shipping quotes server: " . $comm_error;			
	  		return $quotes;
	  	}
	  	
		$result = $this->parse_quotes_xml($xmlResponse);
		//print_r ($result);		  
		$error = $result['error'];
		$quotes;
		
		$quotes = new W2SQuotes(!empty($error));
		// error occured
		$quotes->errorMessage = $error;
								
		for ($i=0; $i<count($result); $i++) {
				
				$response = $result[$i];
				// add a new quote
				$quote = new W2SQuote();
				$quote->totalCharge = $response['totalCharge'];
				$quote->carrierId = $response['carrierId'];
				$quote->carrierName = $response['carrierName'];
				$quote->serviceId = $response['serviceId'];
				$quote->serviceName  = $response['serviceName'];
				$quote->transitDays = $response['transitDays'];
				$quote->estimatedDeliveryDate = $response['estimatedDeliveryDate'];
				
				$quotes->addQuote($quote);
		}

		return $quotes;   	
    }
    
    function filterQuotes($quotes, $carrierID, $servicesEnabled) {
    		
    	if (!is_int($carrierID) && !is_array($servicesEnabled)) {
    		//no filtering requested
    		return $quotes;	
    	}
    	
    	$filtered_quotes = new W2SQuotes($quotes->isError);
    	$filtered_quotes->errorMessage = $quotes->errorMessage;
    	for ($i=0; $i < count($quotes->quotes); $i++) {			
			$quote = $quotes->quotes[$i];

			if (is_int($carrierID) && ($quote->carrierId != $carrierID)) {
    			// not the service we are filtering for
    			continue;
    		}
    		   		
    		if (is_array($servicesEnabled) && (in_array($quote->serviceId, $servicesEnabled) === FALSE)) {
    			// not one of the services we need
    			continue;
    		}
    		// everything is ok - add to quote
    		$filtered_quotes->addQuote($quote);
    		$quotes->isError = false;
    	}

    	return $filtered_quotes;
    }

    function sortQuotes($quotes) {
    	$quotesToSort = $quotes->quotes;   	
    	// in place sort using class compare method 
    	usort($quotesToSort, array("W2SQuote", "compare"));
    	$quotes->quotes = $quotesToSort;
    	return $quotes;	
    }    
    
  	function parse_quotes_xml ($in) {	
		$attributes = array();
		
  		$xml_parser = xml_parser_create ();
		xml_parser_set_option ($xml_parser, XML_OPTION_CASE_FOLDING, 0);
    	xml_parser_set_option ($xml_parser, XML_OPTION_SKIP_WHITE, 1);
    	xml_parse_into_struct ($xml_parser, $in, $attributes, $index);
    	xml_parser_free ($xml_parser);		
		
	
		$out = array();
		reset($attributes);
		while (list ($key, $val) = each ($attributes)) {
	
			$name = $val['tag']; $value = $val['value'];
			if (0 == strcmp($name,'quote')) {
				$out[] = $val['attributes'];
			}
			else if (0 == strcmp($name,'error')) {
				$out['error'] .='<br/>'.$value;
			}	
		}
		return $out;
	}

  	function parse_updates_xml ($in) {	
		$attributes = array();
		
  		$xml_parser = xml_parser_create ();
		xml_parser_set_option ($xml_parser, XML_OPTION_CASE_FOLDING, 0);
    	xml_parser_set_option ($xml_parser, XML_OPTION_SKIP_WHITE, 1);
    	xml_parse_into_struct ($xml_parser, $in, $values);
    	xml_parser_free ($xml_parser);		
		
		$out = array();
		reset($values);
		
		for($i = 0; $i < sizeof($values); $i++) {
			$val = $values[$i];
			
			$value = $val['value'];
						
			if ($val['tag'] == 'order-update' && $val['type'] == 'open') {

				
				$orderID = $val['attributes']['cartOrderId'];
				$status = $val['attributes']['orderStatus'];
				// check if tracking info available
				if ($values[++$i]['tag'] == 'tracking-info') {
					//tracking info is available
					$i++; //skip tracking-numbers tag
					//get the tracking numbers
					$trackingNums = array();
					while($values[++$i]['tag'] == 'tracking-number') {
						$trackingNums[] = $values[$i]['value'];	
					}										
					//get the tracking URL
					$trackingURL = $values[++$i]['value']; 	
				}
				$update = array('orderID' => $orderID, 'status' => $status, 
					'trackingNums' => $trackingNums, 'trackingURL' => $trackingURL);
				//append
				$out[] = $update;								
			}

			
		}
		
		return $out;
	}		
  }
   
  
  class W2SCache {
  	function putInCache($key, $value) {
  		$_SESSION[$key] = serialize($value);  			
  	}
  	
  	function getFromCache($key) {
  		return unserialize($_SESSION[$key]);
  	}
  	
  	function existsInCache($key) {
  		return isset($_SESSION[$key]);  		
  	}
  	
  	function removeFromCache($key) {
  		unset($_SESSION["key"]);	
  	}
  }

  
  
?>
